package edu.vcu.csl.ess;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;

import edu.vcu.csl.ess.RawModel.*;

public class ModelCodeGen {
	public static void output(PrintWriter out, int indentCount, String str) {
		for(int i=0; i<indentCount; i++) 
			out.print("  ");
		out.println(str);
	}

	public static void generateHeader(PrintWriter out) {
		output(out,0,"package proc;");
		output(out,0,"import java.io.BufferedWriter;");
		output(out,0,"import java.io.FileWriter;");
		output(out,0,"import java.io.PrintWriter;");
		output(out,0,"import java.io.IOException;");
		output(out,0,"import java.util.Random;");	
		output(out,0,"");
	}

	public static void generateGlobalSimVarDeclarations(PrintWriter out, 
			RawModel model, 
			String dir) {
		output(out,1,"//Random simulator var declarations");
		output(out,1,"static double __currentTime = 0.0;");
		output(out,1,"static double __nextPrintTime = 0.0;");
		output(out,1,"static double __props[] = new double[" + model.reactions.size() + "];");
		output(out,1,"static int[] __reactionSearchOrder = new int[" + model.reactions.size() + "];");
		output(out,1,"static double __propensityTotal;");
		output(out,1,"static double __oldPropensityTotal;");
		output(out,1,"static double __nextReactionTime;");
		output(out,1,"static int __lastExecutedReactionIndex;");
		output(out,1,"static int __nextEventIndex = 0;");
		output(out,1,"static double __oldProp = 0.0;");
		output(out,1,"static Random __generator = new Random(" + model.seed + ");");
		output(out,1,"static PrintWriter __out;");
		//Trigger stack overflow fix:
		output(out,1,"static int __triggersFired = 0;");
		//End trigger stack overflow fix
		output(out,0,"");
	}


	public static void generateExecuteReactionFunctions(PrintWriter out, RawModel model) {
		for(Reaction reaction : model.reactions) {
			output(out,1,"public static void __reaction_execute_" + 
					reaction.name + "() {");
			for(ReactionElement delta : reaction.delta) 
				output(out,2,"" + delta.species + " += " + delta.coefficient + ";");
			output(out,2,"__" + reaction.name + "_count++;");
			for (Reaction r : reaction.affectedReactionList)
				output(out,2,"__reaction_recalculate_propensity_" + r.name + "();");
			output(out,2,"__trigger_check_" + reaction.name + "();");
			output(out,2,"__magnitudeRecalcCheck();");
			output(out,1,"}");
			output(out,0,"");
		}
	}

	public static void generateDMExecuteReactionFunctions(PrintWriter out, RawModel model) {
		for(Reaction reaction : model.reactions) {
			output(out,1,"public static void __reaction_execute_" + 
					reaction.name + "() {");
			for(ReactionElement delta : reaction.delta) 
				output(out,2,"" + delta.species + " += " + delta.coefficient + ";");
			output(out,2,"__" + reaction.name + "_count++;");			
			output(out,1,"}");
			output(out,0,"");
		}
	}

	public static void generateReactionPropensityFunctions(PrintWriter out, RawModel model) {
		for(Reaction reaction : model.reactions) {
			output(out,1,"public static double __reaction_calculate_propensity_" + 
					reaction.name + "() {");
			String str = "return " + reaction.name;

			for(ReactionElement reactant : reaction.reactants) {
				for(int i=0; i<reactant.coefficient; i++)  {
					if (i == 0) {
						str += "*" + reactant.species;
					} else {
						str += "*(" + reactant.species + "-" + i + ")/" + (i+1);
					}
				}
			}
			str += ";";
			output(out,2,str);
			output(out,1,"}");
			output(out,0,"");
		}	

		for(Reaction reaction : model.reactions) {
			output(out,1,"public static void __reaction_recalculate_propensity_" + 
					reaction.name + "() {");
			int index = model.reactions.indexOf(reaction);
			output(out,2,"__propensityTotal -= __props[" + index + "];");
			output(out,2,"__props[" + index + "] = __reaction_calculate_propensity_" + 
					reaction.name + "();");
			output(out,2,"__propensityTotal += __props[" + index + "];");
			output(out,1,"}");
			output(out,0,"");
		}	

	}

	private static String getTriggerString(RawModel model, 
			ArrayList<Trigger> dependencies) {
		String str = "";
		for(Trigger t : model.triggers) {
			if (dependencies.contains(t))
				str += "1";
			else {
				str += "0";
			}
		}
		return str;
	}

	private static int getTriggerStringIndex(ArrayList<String> triggerStrings,
			String triggerString) {
		int index = -1;
		for(int i=0; i<triggerStrings.size(); i++) {
			if (triggerString.compareTo(triggerStrings.get(i)) == 0) {
				index = i;
				break;
			}
		}
		if (index == -1) {
			index = triggerStrings.size();
			triggerStrings.add(triggerString);
		}
		return index;
	}

	private static void generateTriggerCheckFunction(PrintWriter out,
			RawModel model,
			ArrayList<Trigger> dependencies,
			ArrayList<String> triggerStrings,
			String name) {
		String triggerString = getTriggerString(model,dependencies);
		int triggerStringIndex = getTriggerStringIndex(triggerStrings,triggerString);

		output(out,1,"public static void __trigger_check_" + name + "() {");
		output(out,2,"__trigger_madness_" + triggerStringIndex + "();");
		output(out,1,"}");
		output(out,0,"");	
	}
	
	@SuppressWarnings("unused")
	public static void generateTriggerFunctions(PrintWriter out, RawModel model) {
		ArrayList<String> triggerStrings = new ArrayList<String>();
		int dependenceCount = 0;

		//Generate the all trigger string
		String allTriggerString = "";
		
		for(Trigger r : model.triggers) 
			allTriggerString += "1";
		getTriggerStringIndex(triggerStrings,allTriggerString);


		for(Reaction r : model.reactions) 
			generateTriggerCheckFunction(out, model, r.affectedTriggerList,
					triggerStrings, r.name);
		for(Event r : model.events) 
			generateTriggerCheckFunction(out, model, r.affectedTriggerList,
					triggerStrings, r.name);
		for(Periodic r : model.periodics)
			generateTriggerCheckFunction(out, model, r.affectedTriggerList,
					triggerStrings, r.name);
		for(Trigger r : model.triggers) 
			generateTriggerCheckFunction(out, model, r.affectedTriggerList,
					triggerStrings, r.name);

		//After this loop, triggerStrings has been populated with all possible dependencies based on
		//the affectedTriggerList of each collection.

		//Code that limits the depth of these functions in the call stack with need to be added here
		//as well as above in the generateTriggerCheckFunction method.

		for(int l = 0; l < triggerStrings.size(); l++){	//Fixes exception, logic error?
			//		for(String triggerString : triggerStrings) {

			String triggerString = triggerStrings.get(l);

			int triggerStringIndex = triggerStrings.indexOf(triggerString);
			char triggerStringArray[] = triggerString.toCharArray();

			//			System.out.println("TS = " + triggerString + "(" + triggerStringIndex +")");

			output(out,1,"public static void __trigger_madness_" + triggerStringIndex + "() {");
			for(int i=0; i<triggerString.length(); i++) {
				if (triggerString.charAt(i) == '1') {

					dependenceCount++;

					//Insert depth checking code
					//And/or interconnected-ness checking

					output(out,2,"if (__trigger_condition_" + model.triggers.get(i).name + "()) {");
					output(out,3,"__trigger_action_" + model.triggers.get(i).name + "();");
					String temp = getTriggerString(model,model.triggers.get(i).affectedTriggerList);
					char tempArray[] = temp.toCharArray();
					for(int j=0; j<triggerString.length(); j++) {
						if (triggerStringArray[j] == '1')
							tempArray[j] = '1';
					}
					temp = new String(tempArray);
					//This line may modify triggerStrings
					//causes ConcurrentModificationException under new for loop notation.
					int tempIndex = getTriggerStringIndex(triggerStrings,temp);
					//
					output(out,3,"__trigger_madness_" + tempIndex + "();");
					output(out,3,"return;");
					output(out,2,"}");
					triggerStringArray[i] = '0';
				}
			}
			output(out,1,"}");
			output(out,0,"");
		}

		//This verifies that dependency has nothing to do with stack overflow
		if(model.debug){
			System.out.println("Dependency count = " + dependenceCount);
			System.out.println("Trigger count = " + triggerStrings.size());
		}

		for(Trigger t : model.triggers) {
			output(out,1,"public static boolean __trigger_condition_" + t.name + "() {");
			output(out,2,"return " + t.name + " && (" + t.condition + ");");
			output(out,1,"}");
			output(out,0,"");
		}

		for(Trigger t : model.triggers) {
			output(out,1,"public static void __trigger_action_" + t.name + "() {");
			output(out,2,"" + t.action);
			output(out,2,"__" + t.name + "_count++;");
			for (Reaction r : t.affectedReactionList)
				output(out,2,"__reaction_recalculate_propensity_" + r.name + "();");	    
			output(out,2,"__magnitudeRecalcCheck();");
			output(out,1,"}");
			output(out,0,"");
		}
	}

	public static void generateCalculateAllReactionPropensitiesFunction(PrintWriter out, RawModel model) {

		output(out,1,"public static void __calculate_all_reaction_propensities() {");

		output(out,2,"__propensityTotal = 0;");
		for(int i=0; i<model.reactions.size(); i++) {
			output(out,2,"__props[" + i + "] = " +
					"__reaction_calculate_propensity_" + model.reactions.get(i).name + "();");
			output(out,2,"__propensityTotal += __props[" + i + "];");
		}

		output(out,2,"__oldPropensityTotal = __propensityTotal;");
		output(out,1,"}");
		output(out,0,"");
	}


	public static void generateConstantDeclarations(PrintWriter out, RawModel model) {
		output(out,1,"// Constants");
		for(Constant c : model.constants)				
			output(out,1,"static double " + c.name + " = " + c.value + ";");
		output(out,0,"");
	}

	public static void generateSpeciesDeclarations(PrintWriter out, RawModel model) {
		output(out,1,"// Species");
		for(Species sp : model.species)
			output(out,1,"static double " + sp.name + " = " + sp.population + ";");
		output(out,0,"");	
	}

	public static void generateRateConstantDeclarations(PrintWriter out, RawModel model) {
		output(out,1,"//Rate constant declarations");
		for(Reaction r : model.reactions)
			output(out,1,"static double " + r.name + " = "  + r.rate + ";");		
		output(out,0,"");	
	}

	public static void generateCountDeclarations(PrintWriter out, RawModel model) {
		output(out,1,"// Count declarations");
		for(Reaction r : model.reactions)
			output(out,1,"static int __" + r.name + "_count = "  + "0;");		
		for(Event e : model.events)
			output(out,1,"static int __" + e.name + "_count = "  + "0;");		
		for(Periodic p : model.periodics)
			output(out,1,"static int __" + p.name + "_count = "  + "0;");		
		for(Trigger t : model.triggers)
			output(out,1,"static int __" + t.name + "_count = "  + "0;");		
		output(out,0,"");
	}

	public static void generateEnableDeclarations(PrintWriter out, RawModel model) {
		output(out,1,"// Enable declarations");
		for(Event e : model.events)
			output(out,1,"static boolean " + e.name + " = true;");
		for(Periodic p : model.periodics)
			output(out,1,"static boolean " + p.name + " = true;");
		for(Trigger t : model.triggers)
			output(out,1,"static boolean " + t.name + " = true;");
		output(out,0,"");	
	}

	public static void generateOutputFunction(PrintWriter out, RawModel model) {
		output(out,1,"public static void __output(double time) {");
		output(out,2,"__out.print(time);");
		for (Species s : model.species)
			output(out,2,"__out.print(\",\"+" + s.name + ");");
		for (Reaction r : model.reactions)
			output(out,2,"__out.print(\",\"+__" + r.name + "_count);");
		for (Event e : model.events)
			output(out,2,"__out.print(\",\"+__" + e.name + "_count);");
		for (Periodic p : model.periodics)
			output(out,2,"__out.print(\",\"+__" + p.name + "_count);");
		for (Trigger t : model.triggers)
			output(out,2,"__out.print(\",\"+__" + t.name + "_count);");
		output(out,2,"__out.println();");
		output(out,1,"}");
		output(out,1,"");
	}

	public static void generateOutputHeaderFunction(PrintWriter out, RawModel model) {
		output(out,1,"public static void __output_header() {");
		output(out,2,"__out.print(\"time\");");
		for (Species s : model.species)
			output(out,2,"__out.print(\"," + s.name + "\");");
		for (Reaction r : model.reactions)
			output(out,2,"__out.print(\"," + r.name + "\");");
		for (Event e : model.events)
			output(out,2,"__out.print(\"," + e.name + "\");");
		for (Periodic p : model.periodics)
			output(out,2,"__out.print(\"," + p.name + "\");");
		for (Trigger t : model.triggers)
			output(out,2,"__out.print(\"," + t.name + "\");");
		output(out,2,"__out.println();");
		output(out,1,"}");
		output(out,1,"");
	}

	public static void generateMainStart(PrintWriter out) {
		output(out,1,"public static void main(String args[]) throws Exception {");
		output(out,0,"");
	}

	public static void generateMainInitPrintWriter(PrintWriter out, String dir, RawModel model) {
		output(out,2,"__out = new PrintWriter(new BufferedWriter(new FileWriter(\"" 
				+ dir + model.filename + ".csv\")));");
		output(out,0,"");
	}

	public static void generateMainOutputHeader(PrintWriter out) {
		output(out,2,"__output_header();");
		output(out,0,"");
	}

	public static void generateMainInitReactionSearchOrder(PrintWriter out, RawModel model) {
		output(out,2,"for(int __j=0; __j<" + model.reactions.size() + ";__j++)");
		output(out,3,"__reactionSearchOrder[__j] = __j;");
		output(out,0,"");
	}

	public static void generateMainCalculateReactionPropensities(PrintWriter out) {
		output(out,2,"__calculate_all_reaction_propensities();");	    
		output(out,0,"");
	}

	public static void generateIllgalStateCheckFunction(PrintWriter out, RawModel model){
		output(out,1,"public static void __illegalStateCheck() {");

		for(Species sp : model.species){
			output(out,2,"if (" + sp.name + "< 0) {");
			output(out,3,"System.out.print(\"ILLEGAL STATE @ \");");
			output(out,3,"System.out.print(\"\" + __currentTime + \" : \");");
			output(out,3,"System.out.println(\"" + sp.name + " has negative population.\");");
			output(out,3,"__out.close();");
			output(out,3,"System.exit(1);");
			output(out,2,"}");
		}
		for(Reaction r : model.reactions){
			output(out,2,"if (" + r.name + "< 0) {");
			output(out,3,"System.out.print(\"ILLEGAL STATE @ \");");
			output(out,3,"System.out.print(\"\" + __currentTime + \" : \");");
			output(out,3,"System.out.println(\"" + r.name + " has negative rate constant.\");");
			output(out,3,"__out.close();");
			output(out,3,"System.exit(1);");
			output(out,2,"}");
		}
		output(out,1,"}");
		output(out,1,"");
	}

	public static void generateMagnitudeRecalcCheckFunction(PrintWriter out, RawModel model) {
		output(out,1,"static void __magnitudeRecalcCheck(){");
		output(out,2,"if(__propensityTotal < (__oldPropensityTotal / 2) ||");
		output(out,2,"   __propensityTotal > (2 * __oldPropensityTotal)) {");
		output(out,3,"__calculate_all_reaction_propensities();");
		output(out,2,"}");
		output(out,1,"}");
		output(out,0,"");
	}

	public static void generateExecuteEventFunctions(PrintWriter out, RawModel model) {
		for(Event e : model.events) {
			output(out,1,"static void __event_execute_" + e.name + "() {");
			output(out,2,"if (!" + e.name + ") return;");
			output(out,2,e.action);
			output(out,2,"__" + e.name + "_count++;");
			for (Reaction r : e.affectedReactionList)
				output(out,2,"__reaction_recalculate_propensity_" + r.name + "();");
			output(out,2,"__trigger_check_" + e.name + "();");
			output(out,2,"__magnitudeRecalcCheck();");
			output(out,1,"}");
			output(out,0,"");
		}

		for(Periodic e : model.periodics) {
			output(out,1,"static void __event_execute_" + e.name + "() {");
			output(out,2,"if (!" + e.name + ") return;");
			output(out,2,e.action);
			output(out,2,"__" + e.name + "_count++;");
			for (Reaction r : e.affectedReactionList)
				output(out,2,"__reaction_recalculate_propensity_" + r.name + "();");
			output(out,2,"__trigger_check_" + e.name + "();");
			output(out,2,"__magnitudeRecalcCheck();");	    	 				
			output(out,1,"}");
			output(out,0,"");
		}
	}

	public static void generateDMExecuteEventFunctions(PrintWriter out, RawModel model) {
		for(Event e : model.events) {
			output(out,1,"static void __event_execute_" + e.name + "() {");
			output(out,2,"if (!" + e.name + ") return;");
			output(out,2,e.action);
			output(out,2,"__" + e.name + "_count++;");
			for (Reaction r : e.affectedReactionList)
				output(out,2,"__reaction_recalculate_propensity_" + r.name + "();");			
			output(out,1,"}");
			output(out,0,"");
		}

		for(Periodic e : model.periodics) {
			output(out,1,"static void __event_execute_" + e.name + "() {");
			output(out,2,"if (!" + e.name + ") return;");
			output(out,2,e.action);
			output(out,2,"__" + e.name + "_count++;");
			for (Reaction r : e.affectedReactionList)
				output(out,2,"__reaction_recalculate_propensity_" + r.name + "();");	    	 				
			output(out,1,"}");
			output(out,0,"");
		}
	}

	public static void generateExecuteNextEventFunction(PrintWriter out, RawModel model) {
		ArrayList<Event> sortedEventList = new ArrayList<Event>();

		for(Event e : model.events) 
			sortedEventList.add(e);

		for(Periodic p : model.periodics) {
			double time = Double.parseDouble(p.start);
			double end = Double.parseDouble(p.end);

			while (time <= end){
				Event e = new Event();
				e.name = p.name;				
				e.time = Double.toString(time);
				e.action = p.action;
				e.comment = p.comment;
				sortedEventList.add(e);
				time += Double.parseDouble(p.period);				
			}			
		}

		Collections.sort(sortedEventList, new EventSort());

		if (sortedEventList.size() == 0) {
			output(out,1,"static double __nextEventTime = Double.POSITIVE_INFINITY;");
		} else {
			output(out,1,"static double __nextEventTime = " + sortedEventList.get(0).time + ";");
		}

		output(out,1,"public static void __executeNextEvent() {");
		output(out,2,"switch(__nextEventIndex) {");

		for(int i=0; i<sortedEventList.size(); i++) {
			output(out,3,"case " + i + ":");
			output(out,4,"if (" + sortedEventList.get(i).name + "){");
			output(out,5,"__event_execute_" + sortedEventList.get(i).name + "();");
			output(out,4,"}");
			if (i+1 < sortedEventList.size()) {
				output(out,4,"__nextEventTime = " + sortedEventList.get(i+1).time + ";");
			} else {
				output(out,4,"__nextEventTime = Double.POSITIVE_INFINITY;");
			}
			output(out,4,"break;");
		}
		output(out,2,"}");
		output(out,2,"__nextEventIndex++;");
		output(out,1,"}");
		output(out,0,"");
	}	

	public static void generateExecuteNextReactionFunction(PrintWriter out, RawModel model){
		output(out,1,"static int __selectReactionToExecute() {");
		output(out,2,"double __selector;");
		output(out,2,"int __reactionIndex;");
		output(out,2,"while (true){");
		output(out,3,"__selector = __generator.nextDouble() * __propensityTotal;");
		output(out,3,"for(int __i=0;__i<__props.length;__i++){");
		output(out,4,"__reactionIndex = __reactionSearchOrder[__i];");
		output(out,4,"__selector -= __props[__reactionIndex];");
		output(out,4,"if (__selector <= 0) return __i;");
		output(out,3,"}");
		output(out,2,"}");
		output(out,1,"}");
		output(out,0,"");


		output(out,1,"static void __executeNextReaction(){");
		output(out,2,"int __i = __selectReactionToExecute();");
		output(out,2,"switch(__reactionSearchOrder[__i]){");
		for(int i=0;i<model.reactions.size();i++){
			output(out,3,"case " + i + ":");
			output(out,4,"  __reaction_execute_" + model.reactions.get(i).name + "();");	    
			output(out,4,"break;");			 				
		}
		output(out,2,"}");
		output(out,0,"");

		//Bubble up executed reaction
		output(out,2,"if (__i != 0){");
		output(out,3,"__lastExecutedReactionIndex = __reactionSearchOrder[__i];");
		output(out,3,"__reactionSearchOrder[__i] = __reactionSearchOrder[__i-1];");
		output(out,3,"__reactionSearchOrder[__i-1] = __lastExecutedReactionIndex;");
		output(out,2,"} else {");
		output(out,3,"__lastExecutedReactionIndex = 0;}");
		output(out,1,"}");
		output(out,0,"");
	}

	public static void generateDMExecuteNextReactionFunction(PrintWriter out, RawModel model){
		output(out,1,"static int __selectReactionToExecute() {");
		output(out,2,"double __selector;");
		output(out,2,"int __reactionIndex;");
		output(out,2,"while (true){");
		output(out,3,"__selector = __generator.nextDouble() * __propensityTotal;");
		output(out,3,"for(int __i=0;__i<__props.length;__i++){");		
		output(out,4,"__selector -= __props[__i];");
		output(out,4,"if (__selector <= 0) return __i;");
		output(out,3,"}");
		output(out,2,"}");
		output(out,1,"}");
		output(out,0,"");


		output(out,1,"static void __executeNextReaction(){");
		output(out,2,"int __i = __selectReactionToExecute();");
		output(out,2,"switch(__i){");
		for(int i=0;i<model.reactions.size();i++){
			output(out,3,"case " + i + ":");
			output(out,4,"  __reaction_execute_" + model.reactions.get(i).name + "();");	    
			output(out,4,"break;");			 				
		}
		output(out,2,"}");
		output(out,0,"");

		output(out,1,"}");
		output(out,0,"");
	}

	// TODO: Make all generated files into parameters

	public static void generateOutputFile(RawModel model){


		//Calls to finish setting up the model
		DependencyGen.calculateReactionDeltas(model);
		DependencyGen.calculateModelDependencies(model);

		String dir = "proc/";

		try {
			PrintWriter procClass = new PrintWriter(new BufferedWriter(new FileWriter(dir + model.filename + ".java")));
			generateHeader(procClass);
			output(procClass,0,"public class " + model.filename + " {");

			generateGlobalSimVarDeclarations(procClass, model,dir);
			generateConstantDeclarations(procClass,model);
			generateSpeciesDeclarations(procClass,model);
			generateRateConstantDeclarations(procClass,model);
			generateCountDeclarations(procClass,model);
			generateEnableDeclarations(procClass,model);

			generateOutputHeaderFunction(procClass,model);
			generateOutputFunction(procClass,model);
			generateIllgalStateCheckFunction(procClass,model);
			generateReactionPropensityFunctions(procClass,model);
			generateCalculateAllReactionPropensitiesFunction(procClass,model);
			generateMagnitudeRecalcCheckFunction(procClass,model);
			generateExecuteEventFunctions(procClass,model);
			generateExecuteReactionFunctions(procClass,model);
			generateExecuteNextEventFunction(procClass,model);
			generateExecuteNextReactionFunction(procClass,model);
			generateTriggerFunctions(procClass,model);

			generateMainStart(procClass);
			generateMainInitPrintWriter(procClass, dir, model);
			generateMainOutputHeader(procClass);
			generateMainInitReactionSearchOrder(procClass,model);	
			generateMainCalculateReactionPropensities(procClass);

			output(procClass,2,"// Check all triggers at startup.");
			output(procClass,2,"__trigger_madness_0();");

			output(procClass,2,"while(true) {");		

			output(procClass,3,"__nextReactionTime = __currentTime-(Math.log(__generator.nextDouble()))/__propensityTotal;");
			output(procClass,0,"");

			output(procClass,3,"if (__nextEventTime < __nextReactionTime) {");
			output(procClass,4,"__currentTime = __nextEventTime;");
			output(procClass,3,"} else {");
			output(procClass,4,"__currentTime = __nextReactionTime;");
			output(procClass,3,"}");
			output(procClass,0,"");

			output(procClass,3,"while (__currentTime > __nextPrintTime) {");
			output(procClass,4,"__output(__nextPrintTime);");
			output(procClass,4,"__nextPrintTime += " + model.interval + ";");
			output(procClass,4,"if (__nextPrintTime >= " + model.end + ") {");
			output(procClass,5,"__out.close();");
			output(procClass,5,"System.exit(0);");
			output(procClass,4,"}");
			output(procClass,3,"}");
			output(procClass,0,"");

			output(procClass,3,"if (__currentTime == __nextEventTime) {");
			output(procClass,4,"__executeNextEvent();");
			output(procClass,3,"} else {");
			output(procClass,4,"__executeNextReaction();");
			output(procClass,3,"}");
			output(procClass,0,"");

			output(procClass,3,"__illegalStateCheck();");
			output(procClass,2,"}");
			output(procClass,0,"");

			output(procClass,1,"}");
			output(procClass,0,"}");

			procClass.close();


		} catch (IOException e) {
			e.printStackTrace();
			System.exit(1);
		}		
	}

	public static void generateDirectMethodOutputFile(RawModel model){


		//Calls to finish setting up the model
		DependencyGen.calculateReactionDeltas(model);
		//		DependencyGen.calculateModelDependencies(model);

		String dir = "proc/";

		try {
			PrintWriter procClass = new PrintWriter(new BufferedWriter(new FileWriter(dir + model.filename + ".java")));
			generateHeader(procClass);
			output(procClass,0,"public class " + model.filename + " {");

			generateGlobalSimVarDeclarations(procClass, model,dir);
			generateConstantDeclarations(procClass,model);
			generateSpeciesDeclarations(procClass,model);
			generateRateConstantDeclarations(procClass,model);
			generateCountDeclarations(procClass,model);
			generateEnableDeclarations(procClass,model);

			generateOutputHeaderFunction(procClass,model);
			generateOutputFunction(procClass,model);
			generateIllgalStateCheckFunction(procClass,model);

			generateReactionPropensityFunctions(procClass,model); 
			generateCalculateAllReactionPropensitiesFunction(procClass,model);
			/*			generateMagnitudeRecalcCheckFunction(procClass,model); */
			generateDMExecuteEventFunctions(procClass,model);
			generateDMExecuteReactionFunctions(procClass,model);
			generateExecuteNextEventFunction(procClass,model);
			generateDMExecuteNextReactionFunction(procClass,model);
			/*			generateTriggerFunctions(procClass,model);*/

			generateMainStart(procClass);
			generateMainInitPrintWriter(procClass, dir, model);
			generateMainOutputHeader(procClass);


			output(procClass,2,"while(true) {");		

			// Check all triggers until they are all satisfied.

			output(procClass,3,"boolean triggerMadnessDone = false;");
			output(procClass,3,"while(!triggerMadnessDone) {");
			output(procClass,4,"triggerMadnessDone = true;");

			// check each trigger condition.
			// if true...
			//   fire action..
			//   increment trig counter...			
			//   triggerMadnessDone = false

			for(Trigger t : model.triggers){
				output(procClass,4,"if(" + t.condition + "){");
				output(procClass,5,t.action);
				output(procClass,5,"__" + t.name + "_count++;");
				output(procClass,5,"triggerMadnessDone = false;");
				output(procClass,4,"}");
			}


			output(procClass,3,"}");

			output(procClass,3,"__calculate_all_reaction_propensities();");

			output(procClass,3,"__nextReactionTime = __currentTime-(Math.log(__generator.nextDouble()))/__propensityTotal;");
			output(procClass,0,"");

			output(procClass,3,"if (__nextEventTime < __nextReactionTime) {");
			output(procClass,4,"__currentTime = __nextEventTime;");
			output(procClass,3,"} else {");
			output(procClass,4,"__currentTime = __nextReactionTime;");
			output(procClass,3,"}");
			output(procClass,0,"");

			output(procClass,3,"while (__currentTime > __nextPrintTime) {");
			//			output(procClass,4,"__output(__nextPrintTime);");
			output(procClass,4,"__nextPrintTime += " + model.interval + ";");
			output(procClass,4,"if (__nextPrintTime >= " + model.end + ") {");
			output(procClass,5,"__out.close();");
			output(procClass,5,"System.exit(0);");
			output(procClass,4,"}");
			output(procClass,3,"}");
			output(procClass,0,"");

			// Execute either the event or the reaction - WITHOUT DEPENDENDCY GRAPH


			output(procClass,3,"if (__currentTime == __nextEventTime) {");
			output(procClass,4,"__executeNextEvent();");
			output(procClass,3,"} else {");
			output(procClass,4,"__executeNextReaction();");
			output(procClass,3,"}");
			output(procClass,0,"");



			output(procClass,3,"__illegalStateCheck();");
			output(procClass,2,"}");
			output(procClass,0,"");

			output(procClass,1,"}");
			output(procClass,0,"}");

			procClass.close();


		} catch (IOException e) {
			e.printStackTrace();
			System.exit(1);
		}		
	}


}
